/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package de.saar.penguin.give2.mapviewer;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import de.saar.basic.Pair;
import de.saar.basic.swing.Draw;
import give.formula.Atom;
import give.world.World;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author koller
 */
public class ControllerArrowsPlugin implements MapAnnotationPlugin, MouseOverObjectListener {

    private static final Color arrowColor = new Color(0x228b22);
    private static final Color futureArrowColor = new Color(0x80e080);
    private static final Color NUMBER_BACKGROUND = new Color(1.0f, 1.0f, 1.0f, 0.8f);
    private static final Stroke stroke = new BasicStroke(4);
    private static final Stroke THIN_STROKE = new BasicStroke(1);
    private static final int NEXT_SAFE_BUTTON_RADIUS = 30;
    private JMapViewer viewer;
    private World world;
    private List<Pair<Point2D, Point2D>> arrows;
    private List<Pair<Point2D, Point2D>> futureSafeArrows;
    private ListMultimap<String, String> controls, controlledBy;
    private ListMultimap<String, String> safeSequences;
    private Map<String, Integer> activeFutureSafeButtons;
    private Set<String> buttonsWithStuffDrawnInThem;
    private Set<String> nextSafeButtons;
    private ListMultimap<String, String> buttonsToSafes;

    public ControllerArrowsPlugin(World world, JMapViewer viewer) {
        this.viewer = viewer;
        this.world = world;

        arrows = new ArrayList<Pair<Point2D, Point2D>>();
        futureSafeArrows = new ArrayList<Pair<Point2D, Point2D>>();
        activeFutureSafeButtons = new HashMap();
        buttonsWithStuffDrawnInThem = new HashSet<String>();
        nextSafeButtons = new HashSet<String>();
        buttonsToSafes = ArrayListMultimap.create();

        controls = ArrayListMultimap.create();
        controlledBy = ArrayListMultimap.create();
        safeSequences = ArrayListMultimap.create();


        for (Atom a : world.getTrueAtoms()) {
            if (a.getPredicate().equals("controls")) {
                String x = a.getArgumentAsString(0), y = a.getArgumentAsString(1);

                controls.put(x, y);
                controlledBy.put(y, x);
            }
        }

        for (String s : world.getUniverse()) {
            if ("safe".equals(world.getType(s))) {
                List<Atom> safeSequenceAtoms = new ArrayList<Atom>();

                for (Atom a : world.getTrueAtoms()) {
                    if (a.getPredicate().equals("manipulate-effect-state-change") && a.getArgumentAsString(1).equals(s) && !a.getArgumentAsString(3).equals("safe-state-1")) {
                        safeSequenceAtoms.add(a);
                    }
                }

                int state = 1;

                safeSequenceLoop:
                while (true) {
                    String thisState = "safe-state-" + state;
                    String nextState = "safe-state-" + (state + 1);

                    for (Atom a : safeSequenceAtoms) {
                        if (a.getArgumentAsString(2).equals(thisState) && (a.getArgumentAsString(3).equals(nextState) || a.getArgumentAsString(3).equals("safe-state-open"))) {
                            String buttonName = a.getArgumentAsString(0);
                            safeSequences.put(s, buttonName);
                            buttonsToSafes.put(buttonName, s);

                            if (a.getArgumentAsString(3).equals("safe-state-open")) {
                                break safeSequenceLoop;
                            } else {
                                state++;
                                continue safeSequenceLoop;
                            }
                        }
                    }
                }
            }
        }
    }

    public void paint(Graphics2D graphics, JMapViewer mapViewer) {
        buttonsWithStuffDrawnInThem.clear();

        graphics.setStroke(stroke);

        // draw arrows to future buttons
        graphics.setColor(futureArrowColor);
        for (Pair<Point2D, Point2D> arrow : futureSafeArrows) {
            Draw.arrow((int) arrow.left.getX(), (int) arrow.left.getY(), (int) arrow.right.getX(), (int) arrow.right.getY(), 10, graphics);
        }

        // draw arrows to next button
        graphics.setColor(arrowColor);
        for (Pair<Point2D, Point2D> arrow : arrows) {
            Draw.arrow((int) arrow.left.getX(), (int) arrow.left.getY(), (int) arrow.right.getX(), (int) arrow.right.getY(), 10, graphics);
        }

        graphics.setStroke(THIN_STROKE);

        // draw numbers for future buttons
        for (String activeSafe : activeFutureSafeButtons.keySet()) {
            boolean first = true;

            for (int i = activeFutureSafeButtons.get(activeSafe) - 1; i < safeSequences.get(activeSafe).size(); i++) {
                String button = safeSequences.get(activeSafe).get(i);
                Point2D pos = viewer.getCenter(button);

                if (first) {
                    // draw a circle around the next button
                    first = false;

                    graphics.setColor(arrowColor);
                    graphics.drawOval((int) pos.getX() - NEXT_SAFE_BUTTON_RADIUS / 2, (int) pos.getY() - NEXT_SAFE_BUTTON_RADIUS / 2, NEXT_SAFE_BUTTON_RADIUS, NEXT_SAFE_BUTTON_RADIUS);
                }

                if (buttonsWithStuffDrawnInThem.add(button)) {
                    paintNumberOnButton((int) pos.getX(), (int) pos.getY(), Integer.toString(i + 1), graphics);
                }
            }
        }
    }

    private void paintNumberOnButton(int midx, int midy, String s, Graphics2D g2) {
        FontRenderContext frc = g2.getFontRenderContext();
        Font font = g2.getFont();
        int sw = (int) font.getStringBounds(s, frc).getWidth();
        LineMetrics lm = font.getLineMetrics(s, frc);
        int sh = (int) (lm.getAscent() + lm.getDescent());

        g2.setColor(NUMBER_BACKGROUND);
        g2.fillRect(midx - sw / 2, midy - sh / 2, sw, sh);

        g2.setColor(Color.black);
        g2.drawString(s, midx - 4, midy + 6);
    }

    public void mouseEnteredObject(int x, int y, Collection<String> objects) {
        arrows.clear();
        futureSafeArrows.clear();
        nextSafeButtons.clear();
        activeFutureSafeButtons.clear();

        for (String o : objects) {
            for (String controllee : controls.get(o)) {
                addArrow(o, controllee);
            }

            for (String controller : controlledBy.get(o)) {
                addArrow(controller, o);
            }

            if (safeSequences.containsKey(o)) {
                markupSafeSequenceButtons(o);
            }

            if (buttonsToSafes.containsKey(o)) {
                for (String safe : buttonsToSafes.get(o)) {
                    markupSafeSequenceButtons(safe);
                }
            }
        }

        viewer.repaint();
    }

    private void addArrow(String x, String y) {
        arrows.add(new Pair(viewer.getCenter(x), viewer.getCenter(y)));
    }

    private void markupSafeSequenceButtons(String safe) {
        String safeState = world.getState(safe);
        int safeStateIndex = -1;

        try {
            safeStateIndex = Integer.parseInt(safeState.substring(11, 12)) - 1;
        } catch (NumberFormatException e) {
        }

        if (safeStateIndex >= 0) {
            List<String> sequence = safeSequences.get(safe);
            String button = sequence.get(safeStateIndex);
            addArrow(button, safe);
            nextSafeButtons.add(button);
            activeFutureSafeButtons.put(safe, safeStateIndex + 1);

            for (int i = safeStateIndex + 1; i < sequence.size(); i++) {
                String futureButton = sequence.get(i);
                futureSafeArrows.add(new Pair(viewer.getCenter(futureButton), viewer.getCenter(safe)));
            }
        }

    }
}
